#include <IceUtil/IceUtil.h>
#include <IceUtil/Thread.h>
#include <Ice/Ice.h>
#include <Demo.h>
#include <stdio.h>
#include <list>

using namespace std;
using namespace Ice;
using namespace Demo;

class HelloI:
  public Demo::Hello
{
public:
  
  HelloI(const string&, int);
  ~HelloI();
  virtual void sayHello(const ::Ice::Current&) const;

private:

  const string _name;
  const int _id;
};

HelloI::HelloI(const string& name,
	       int id):
  _name(name),
  _id(id)
{
}

HelloI::~HelloI()
{
  cout << "Hello object #" << _id << " for session " << _name << " destroyed." << endl;
}

void
HelloI::sayHello(const ::Ice::Current&) const
{
  cout << "Hello object #" << _id << " for session " << _name << " says:\n" 
       << "Hello " << _name << "!" << endl;
}

class SessionI:
  public Demo::Session,
  public IceUtil::Mutex
{
public:
  
  SessionI(const string&);

  virtual ::Demo::HelloPrx create(const ::Ice::Current&);
  virtual std::string getName(const ::Ice::Current&) const;
  virtual void destroy(const ::Ice::Current&);
  virtual void refresh(const ::Ice::Current&);

  IceUtil::Time timespamp() const;

private:

  const string _name;
  IceUtil::Time _timespamp;
  int _nextId;
  std::list<Demo::HelloPrx> _objs;
  bool _destroy;
};

typedef IceUtil::Handle<SessionI> SessionIPtr;

SessionI::SessionI(const string& name):
  _name(name),
  _nextId(0),
  _timespamp(IceUtil::Time::now()),
  _destroy(false)
{
  cout << "The session " << _name << " is now created." << endl;
}

::Demo::HelloPrx
SessionI::create(const ::Ice::Current& current)
{
  Lock sync(*this);
  if (_destroy)
    {
      throw Ice::ObjectNotExistException(__FILE__, __LINE__);
    }
  Demo::HelloPrx hello = Demo::HelloPrx::
    uncheckedCast(current.adapter->
		  addWithUUID(new HelloI(_name, _nextId++)));
  _objs.push_back(hello);
  return hello;
}

std::string
SessionI::getName(const ::Ice::Current&) const
{
  Lock sync(*this);
  if (_destroy)
    {
      throw Ice::ObjectNotExistException(__FILE__, __LINE__);
    }
  return _name;
}

void
SessionI::destroy(const ::Ice::Current& current)
{
  Lock sync(*this);
  if (_destroy)
    {
      throw Ice::ObjectNotExistException(__FILE__, __LINE__);
    }
  _destroy = true;
  cout << "The session " << _name << " is now destroyed." << endl;
  try
    {
      current.adapter->remove(current.id);
      for (std::list<HelloPrx>::const_iterator p = _objs.begin();
	   p != _objs.end();
	   ++p)
	{
	  current.adapter->remove((*p)->ice_getIdentity());
	}
    }
  catch (const Ice::ObjectAdapterDeactivatedException&)
    {
    }
  _objs.clear();
}

void
SessionI::refresh(const ::Ice::Current&)
{
  Lock sync(*this);
  if (_destroy)
    {
      throw Ice::ObjectNotExistException(__FILE__, __LINE__);
    }
  cout << "Refreshing..." << endl;
  _timespamp = IceUtil::Time::now();
}

IceUtil::Time
SessionI::timespamp() const
{
  Lock sync(*this);
  if (_destroy)
    {
      throw Ice::ObjectNotExistException(__FILE__, __LINE__);
    }
  return _timespamp;
}

class ReapThread:
  public IceUtil::Thread,
  public IceUtil::Monitor<IceUtil::Mutex>
{
public:

  ReapThread();
  virtual void run();
  void terminate();
  void add(const Demo::SessionPrx&,
	   const SessionIPtr&);

private:

  const IceUtil::Time _timeout;
  bool _terminated;
  struct SessionProxyPair
  {
    SessionProxyPair(const Demo::SessionPrx& p,
		     const SessionIPtr& s):
      proxy(p), session(s) {}
    const Demo::SessionPrx& proxy;
    const SessionIPtr& session;
  };
  std::list<SessionProxyPair> _sessions;
};

typedef IceUtil::Handle<ReapThread> ReapThreadPtr;

ReapThread::
ReapThread():
  _timeout(IceUtil::Time::seconds(10)),
  _terminated(false)
{
}

void
ReapThread::run()
{
  Lock sync(*this);

  while (!_terminated)
    {
      timedWait(_timeout);
      if (!_terminated)
	{
	  std::list<SessionProxyPair>::iterator p = _sessions.begin();
	  while (p != _sessions.end())
	    {
	      try
		{
		  if ((IceUtil::Time::now() - p->session->timespamp())
		      > _timeout)
		    {
		      string name = p->proxy->getName();
		      p->proxy->destroy();
		      cout << "The session " << name << "has timed out." << endl;
		      p = _sessions.erase(p);
		    }
		  else
		    {
		      ++p;
		    }
		}
	      catch (const Ice::ObjectNotExistException&)
		{
		  p = _sessions.erase(p);
		}
	    }
	}
    }
}

void
ReapThread::terminate()
{
  Lock sync(*this);

  _terminated = true;
  notify();

  _sessions.clear();
}

void
ReapThread::add(const Demo::SessionPrx& proxy,
	       const SessionIPtr& session)
{
  Lock sync(*this);
  _sessions.push_back(SessionProxyPair(proxy, session));
}

class SessionFactoryI:
  public Demo::SessionFactory
{
public:

  SessionFactoryI(const ReapThreadPtr&);
  virtual Demo::SessionPrx create(const ::std::string&, const ::Ice::Current&);
  virtual void shutdown(const ::Ice::Current&);

private:

  ReapThreadPtr _reaper;
};

SessionFactoryI::SessionFactoryI(const ReapThreadPtr& readThread):
  _reaper(readThread)
{
}

Demo::SessionPrx
SessionFactoryI::create(const ::std::string& name, const ::Ice::Current& current)
{
  SessionIPtr session = new SessionI(name);
  SessionPrx proxy = SessionPrx::uncheckedCast(current.adapter->addWithUUID(session));
  _reaper->add(proxy, session);
  return proxy;
}

void
SessionFactoryI::shutdown(const ::Ice::Current& current)
{
  cout << "Shutting down..." << endl;
  current.adapter->getCommunicator()->shutdown();
}

class Server : virtual public Ice::Application
{
  virtual int run (int, char*[])
  {
    Ice::ObjectAdapterPtr adapter = communicator()->
      createObjectAdapterWithEndpoints("SessionFactoryAdapter", "default -p 10000");

    ReapThreadPtr reaper = new ReapThread();
    reaper->start();

    Ice::ObjectPtr object = new SessionFactoryI(reaper);
    adapter->add(object, communicator()->stringToIdentity("SessionFactory"));
    adapter->activate();
    communicator()->waitForShutdown();

    reaper->terminate();
    reaper->getThreadControl().join();

    return EXIT_SUCCESS;
  }
};

int
main(int argc, char* argv[])
{
  Server server;
  return server.main(argc, argv);
}
